/**
 * 
 */
package com.populardata;


import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.junit.Before;

import junit.framework.TestCase;
// originally created with JUnit 4.3.1

/**
 * @author bshirley
 *
 */
public class TestAddressLookup
	extends TestCase {
	
	public void testCapitalizeFirstLetterOfNames() {
		assertEquals("empty", AddressLookup.initialCaps(""), "");
		assertEquals("space only", AddressLookup.initialCaps(" "), " ");
		assertEquals("one word - from caps", AddressLookup.initialCaps("FOO"), "Foo");
		assertEquals("one word - to caps", AddressLookup.initialCaps("foo"), "Foo");
		assertEquals("trailing space", AddressLookup.initialCaps("FOO "), "Foo ");
		assertEquals("leading space", AddressLookup.initialCaps(" FOO"), " Foo");
		assertEquals("space around", AddressLookup.initialCaps(" FOO "), " Foo ");
		assertEquals("two words", AddressLookup.initialCaps("FOO BAR"), "Foo Bar");
		assertEquals("five words", AddressLookup.initialCaps("FOO BAR BAZ BANG BEWILDERED"), 
					"Foo Bar Baz Bang Bewildered");
	}
	
	public void testZipLookup() {
		addressLookup.primeTheData();
		AddressLookup z = addressLookup.lookupZip(77007);
		assertEquals(z.getCity(), "Houston");
		assertEquals(z.getCounty(), "Harris");
		assertEquals(z.getState(), "Texas");
	}
	
	public void testAllStateNames() {
		Set<String> states = addressLookup.getAllStateNames(false);
		assertEquals("Check state + others count", 62, states.size());

		states = addressLookup.getAllStateNames(true);
		assertEquals("Check state + others count", 51, states.size());
	}

	public void testCityNamesForState() {
		Set<String> cities = addressLookup.getCityNamesForState("Texas");
		assertEquals("Texas has 1481 sities", 1481, cities.size());
		
		cities = addressLookup.getCityNamesForState("Delaware");
		assertEquals("Delaware has 57 cities", 57, cities.size());

		cities = addressLookup.getCityNamesForState("Puerto Rico");
		assertEquals("Puerto Rico has 99 cities", 99, cities.size());
	}
	
	public void testAddressLookupsForState() {
		long before, after;
		
		before = System.currentTimeMillis();
		Set<AddressLookup> texas = addressLookup.getAddressLookupsForState("Texas");
		after = System.currentTimeMillis();
		if ((after-before) > 1000)
			System.err.println("fetching " + texas.size() + " AddressLookup instances in " + (after-before) + " milliseconds.");
		assertTrue("Expecting fetch in less than 1 second", (after-before) < 1000 );
		
		assertEquals("Texas has 2682 zip codes", 2682, texas.size());
		
		before = System.currentTimeMillis();
		Map<Integer, AddressLookup> zipMap = new HashMap<Integer, AddressLookup>(texas.size());
		for (AddressLookup a : texas)
			zipMap.put(a.getZip(), a);
		after = System.currentTimeMillis();
		if ((after-before) > 100)
			System.err.println("built map of " + texas.size() + " in " + (after-before) + " milliseconds.");
		
		assertTrue("expect certain zip codes ", zipMap.containsKey(77002));
		assertTrue("expect certain zip codes ", zipMap.containsKey(77007));
		assertTrue("expect certain zip codes ", zipMap.containsKey(77008));
		assertTrue("expect certain zip codes ", zipMap.containsKey(77009));
		assertTrue("expect certain zip codes ", zipMap.containsKey(77098));
		
		for (AddressLookup a : texas) {
			int zip = a.getZip();
			assertEquals("every zip is in Texas " + zip + " " + a.getState() , "Texas", a.getState());
			assertTrue("texas zips range failed for " + zip, zip >= 73301 && zip <= 88595);
		}
		
		
	}
	
	public void testAddressLookupsFromCity() {
		Set<AddressLookup> houston = addressLookup.getAddressLookupsForCity("Texas", "Houston");
		
		assertEquals("Houston has x zips", 181, houston.size());
		
		for (AddressLookup a : houston) {
			int zip = a.getZip();
			assertEquals("every zip is in Houston " + zip + " " + a.getCity(), "Houston", a.getCity());
			assertEquals("every zip is in Texas " + zip + " " + a.getState(), "Texas", a.getState());
			assertTrue("houston zip range failed for " + zip, zip >= 77000 && zip <= 77299);
			
			// this data is a little rough - if it got refined, it might break
			float lat = a.getLatitude(), lon = a.getLongitude();
			assertTrue("latitude in range for " + zip + " " + lat, lat > 29.571 && lat < 29.835);
			assertTrue("longitude in range for " + zip + " " + lon, lon > -95.435 && lon < -95.099);
		}
	}

	public void testAddressLookupsNear() {
		// if the calculations for distance are tweaked or the data is improved, these numbers will break
		
		Set<AddressLookup> nearby = addressLookup.getAddressLookupsNear((float)29.6, (float)-95.3, 5);
		assertEquals("5 miles - granularity too small", 0, nearby.size());
		
		nearby = addressLookup.getAddressLookupsNear((float)29.6, (float)-95.3, 15);
		assertEquals("15 miles - granularity too small", 0, nearby.size());
		
		nearby = addressLookup.getAddressLookupsNear((float)29.6, (float)-95.3, 50);
		assertEquals("50 miles - granularity too small", 0, nearby.size());

		nearby = addressLookup.getAddressLookupsNear((float)29.6, (float)-95.3, 150);
		assertEquals("150 miles", 397, nearby.size());

		nearby = addressLookup.getAddressLookupsNear((float)30.275, (float)-97.74, 400);
		assertEquals("400 miles around Austin", 1822, nearby.size());

		for (AddressLookup a : nearby) {
			assertEquals("400 miles from Austin still in Texas " + + a.getZip() + "/" + a.getState() + 
					"(" + a.getLatitude() + "," + a.getLongitude() + ")" ,
					"Texas", a.getState());
		}

	}
	
	public void testLargeReturnValues() {
		long begin = System.currentTimeMillis();
		Set<AddressLookup> large = addressLookup.getAddressLookupsNear(32, -95, 1200);
		
		
		Set<String> visited = new HashSet<String>();
		for (AddressLookup a : large) {
			if (visited.contains(a.getState()) == false) {
				visited.add(a.getState());
				System.out.print(a.getState() + " ");
			}			
		}
		long end = System.currentTimeMillis();
		System.out.println("\n" + large.size() + " items fetched.");
		System.out.println(large.size() * 1000 / (end-begin) + " items processed per second.");
		System.out.println(visited.size() + " states visited");

	}
	
	//
	// 
	//
	
	protected static AddressLookupRemote addressLookup;

	@Before
	public void setUp() throws Exception {
		if (addressLookup == null)
			addressLookup = getZipCodeLookupRemote();
	}

	/**
	 * @return	the AddressAccessRemote object we need to perform testing on
	 */
	static private AddressLookupRemote getZipCodeLookupRemote() {
		Object ref = getRef("AddressLookupBean/remote");
		AddressLookupRemote lookup = (AddressLookupRemote) 
			PortableRemoteObject.narrow(ref, AddressLookupRemote.class);

		return lookup;
	}

	static private Object getRef(String jndiName) {
		
		try {
			Context jndiContext = new InitialContext();
			return jndiContext.lookup(jndiName);
		} catch (NamingException ne) {
			ne.printStackTrace();
		}
		
		return null;
	}


}
